﻿/*
   Open Intel 
   Copyright © 2011 – ISC. All Rights Reserved.
*/

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Windows.Threading;
using Caliburn.Micro;
using ISC.MapDotNetServer.Common.Data;
using ISC.MapDotNetServer.Common.Maps;
using OI.Async;
using OI.Events;
using OI.Framework;

namespace OI.ViewModels
{
    [Export(typeof(MashupViewModel))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class MashupViewModel : Conductor<IScreen>.Collection.AllActive, 
        IHandle<QueriedResultSets>, 
        IHandle<ChangeSearchText>,
        IHandle<ChangeLayerSelection>,
        IHandle<ChangeMapScale>,
        IHandle<EnablePhonebook>
    {
        #region DATA
        readonly private IEventAggregator Events;
        public bool DataAreaExpanded
        {
            get
            {
                return _DataAreaExpanded;
            }
            set
            {
                _DataAreaExpanded = value;
                NotifyOfPropertyChange(() => DataAreaExpanded);
            }
        }
        private bool _DataAreaExpanded;

        public bool LegendAreaExpanded
        {
            get
            {
                return _LegendAreaExpanded;
            }
            set
            {
                _LegendAreaExpanded = value;
                NotifyOfPropertyChange(() => LegendAreaExpanded);
            }
        }
        private bool _LegendAreaExpanded = true;

        public Layer ActiveSearchLayer { get;  private set; }
        public string[] ActiveSearchColumns { get; private set; }
        public string ActiveSearchString { get; private set; }
        public bool IsAddressSearchActive { get; private set; }

        public DispatcherTimer SearchTimer
        {
            get
            {
                if (_SearchTimer == null)
                {
                    _SearchTimer = new DispatcherTimer {Interval = TimeSpan.FromSeconds(0.66)};
                    _SearchTimer.Tick += new EventHandler(SearchTimerTick);
                }
                return _SearchTimer;
            }
        }
        private DispatcherTimer _SearchTimer;
        #endregion

        // sub views
        public MashupLegendViewModel LegendArea { get; private set; }
        public MashupToolboxViewModel ToolboxArea { get; private set; }
        public MashupMapViewModel MapArea { get; private set; }
        public MashupDataViewModel DataArea { get; private set; }


        // displays current status of map
        public string MapStatus
        {
            get { return _MapStatus; }
            set 
            { 
                _MapStatus = value;
                NotifyOfPropertyChange(() => MapStatus);
            }
        }
        private string _MapStatus;


        [ImportingConstructor]
        public MashupViewModel(IEventAggregator events)
        {
            events.Subscribe(this);
            Events = events;
        }


         // when the entire mashup is activated, load child view models
        protected override void OnActivate()
        {
            // set child views
            // get all of the layer ids from the mashup list
            var dop = IoC.Get<DatasetDetailsOptionsPanelViewModel>();
            var list = new List<string>();
            dop.MashupList.Apply((x) => list.Add(x.LayerID));

            MapArea = IoC.Get<MashupMapViewModel>();
            MapArea.LayerIDs = list;
            
            LegendArea = IoC.Get<MashupLegendViewModel>("MashupLegendViewModel");
            ToolboxArea = IoC.Get<MashupToolboxViewModel>();
            DataArea = IoC.Get<MashupDataViewModel>();
            
            Items.Add(MapArea);
            Items.Add(LegendArea);
            Items.Add(ToolboxArea);
            Items.Add(DataArea);

            base.OnActivate();
        }


        // display information about the map
        public void Handle(ChangeMapScale message)
        {
            MapStatus = "zoom level: " +
                message.ZoomLevel.ToString("F1") +
                " | map scale: " +
                message.Scale.ToString("F0") +
                " to 1";
        }


        // if a query result set comes in, go ahead and expand data area
        public void Handle(QueriedResultSets message)
        {
            bool expand = false;
            if (message.Tables != null && message.ShowInDataArea)
            {
                expand = message.Tables.Any(t => t.QueryResultRows.Count > 0);
            }
            DataAreaExpanded = expand;
        }


        // enabling the phone book disables the selected layer - coordinated here
        public void Handle(EnablePhonebook message)
        {
            IsAddressSearchActive = message.Value;

            if (message.Value)
            {
                Events.Publish(new ClearSelectedLayer());

                // initialize search box
                Events.Publish(new ConfigureSearchBox()
                {
                    IsEnabled = true,
                    Watermark = "SEARCH Street Address",
                    InitialText = ""
                });
            }
        }


        // receives message when legend layer selected
        public void Handle(ChangeLayerSelection message)
        {
            if (message.IsSelected)
            {
                ActiveSearchLayer = message.Layer;

                // selecting a layer disables the phonebook
                Events.Publish(new EnablePhonebook
                {
                    Value = false
                });

                // check for metadata
                if (ActiveSearchLayer.HasSearchColumns())
                {
                    ActiveSearchColumns = ActiveSearchLayer.GetSearchColumns().Split(',');

                    if (ActiveSearchColumns.Length > 0)
                    {
                        string layerName = ActiveSearchLayer.GetLayerName(ActiveSearchLayer.ID);
                        
                        // initialize search box
                        Events.Publish(new ConfigureSearchBox()
                        {
                            IsEnabled = true,
                            Watermark = "SEARCH " + layerName,
                            InitialText = ""
                        });
                        return;
                    }
                }

                // there was no metadata for searching
                Events.Publish(new ConfigureSearchBox()
                {
                    IsEnabled = false,
                    Watermark = "this layer is not searchable",
                    InitialText = ""
                });
            }
            else
            {
                ActiveSearchLayer = null;
            }
        }


        // receives message from freeform search panel
        public void Handle(ChangeSearchText message)
        {
            SearchTimer.Stop();

            if (IsActive)
            {
                // if we have a search string
                string sMsg = message.SearchString;
                if (!string.IsNullOrEmpty(sMsg))
                {
                    ActiveSearchString = sMsg;
                    SearchTimer.Start();
                }
            }
        }


        // used to prevent rapid searches on typing
        void SearchTimerTick(object sender, EventArgs e)
        {
            SearchTimer.Stop();
             Coroutine.BeginExecute(Search());
        }


        // coroutine for async search
        public IEnumerator<IResult> Search()
        {
            List<QueryResultTable> tables = null;

            // layer search
            if (!IsAddressSearchActive &&
                ActiveSearchLayer != null &&
                ActiveSearchColumns != null)
            {
                // where clause is all searchable columns with LIKE search string
                string like = " LIKE '%" + ActiveSearchString + "%' ";
                var sb = new StringBuilder();
                ActiveSearchColumns.Apply( sc =>
                {
                    sb.Append(sc.Trim());
                    sb.Append(like);
                    sb.Append(" OR ");
                });
                string where = sb.ToString();
                where = where.Substring(0, where.Length - 4);

                var sq = new MDNMashupSearchQuery(ActiveSearchLayer, where);
                yield return sq;

                tables = sq.Results;
            }
            // address search
            else if (IsAddressSearchActive &&
                     Utility.IsValidStreetAddress(ActiveSearchString))
            {
                var bg = new BingGeocode(ActiveSearchString);
                yield return bg;

                tables = new List<QueryResultTable> {bg.Results};
            }

            // if we have results
            if (tables != null && tables.Count > 0)
            {
                // if we have a single address, go directly to it
                if (IsAddressSearchActive &&
                    tables[0].QueryResultRows.Count == 1)
                {
                    var row = tables[0].QueryResultRows[0];

                    // zoom to it
                    Events.Publish(new ZoomToShape
                    {
                        Shape = row.Shape
                    });

                    // highlight it as a one-shot - which means any other shown shape will clear it
                    Events.Publish(new ShowShape
                    {
                        ID = Guid.NewGuid(),
                        ShowShapeType = ShowShape.ShowShapeTypes.Highlight,
                        ShapeSerial = row.Shape.Serialize(),
                        OneShot = true
                    });
                }
                else
                {
                    // broadcast the arrival of query results
                    Events.Publish(new QueriedResultSets
                    {
                        Sources = new List<Layer> { ActiveSearchLayer },
                        Tables = tables,
                        ShowInDataArea = true
                    });
                }
            }
        }
    }
}
